﻿using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;
using System.Security.Cryptography;
using System.Windows.Forms;

namespace PLock {
	class Settings {
		private Settings() { }

		public static readonly Settings Instance = new Settings();

		public string DataFolder = "";
		public int GeneratedPassLength = 16;
		public bool GeneratePassAZLower = true;
		public bool GeneratePassAZUpper = false;
		public bool GeneratePass09 = true;
		public bool GeneratePassSymbols = false;
		public bool SyncExternal = false;
		public string SyncDrive = "";
		public bool SyncStandalone = false;

		private bool _portable = false;
		public bool Portable {
			get { return _portable; }
			set {
				_portable = value;
				if (value)
					DataFolder = string.Format("{0}{1}{2}", Path.GetDirectoryName(Application.ExecutablePath),
															Path.DirectorySeparatorChar, SADirectory);
			}
		}
		public bool Standalone = true;

		public bool Valid {
			get { return DataFolder != ""; }
		}

		public string FullPath(string filename) {
			return string.Format("{0}{1}{2}", DataFolder, Path.DirectorySeparatorChar, filename);
		}

		private const string RegistryKey = @"Software\PLock\Settings";
		private const string XMLFile = "plock_settings.xml";
		public const string SADirectory = "PLock_Data";

		public void Load() {
			if (_portable)
				LoadFromXML(Path.GetDirectoryName(Application.ExecutablePath));
			else
				LoadFromRegistry();
		}

		public void Save() {
			if (_portable)
				SaveToXML(Path.GetDirectoryName(Application.ExecutablePath));
			else
				SaveToRegistry();
		}

		private void LoadFromRegistry() {
			using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegistryKey)) {
				DataFolder = key.GetValue("DataFolder", "").ToString();
				GeneratedPassLength = (int)key.GetValue("GeneratedPassLength", 16);
				GeneratePassAZLower = (int)key.GetValue("GeneratePassAZLower", 1) != 0;
				GeneratePassAZUpper = (int)key.GetValue("GeneratePassAZUpper", 0) != 0;
				GeneratePass09 = (int)key.GetValue("GeneratePass09", 1) != 0;
				GeneratePassSymbols = (int)key.GetValue("GeneratePassSymbols", 0) != 0;
				SyncExternal = (int)key.GetValue("SyncExternal", 0) != 0;
				SyncDrive = key.GetValue("SyncDrive", "").ToString();
				SyncStandalone = (int)key.GetValue("SyncStandalone", 0) != 0;
			}
		}

		private void LoadFromXML(string directory) {
			if (!directory.EndsWith(Path.DirectorySeparatorChar.ToString()))
				directory += Path.DirectorySeparatorChar;
			if (!File.Exists(directory + XMLFile))
				return;
			XmlDocument doc = new XmlDocument();
			doc.Load(directory + XMLFile);

			XmlNode node = doc.SelectSingleNode("Settings/GeneratedPassLength");
			if (node != null)
				GeneratedPassLength = Convert.ToInt32(node.InnerText);
			node = doc.SelectSingleNode("Settings/GeneratePassAZLower");
			if (node != null)
				GeneratePassAZLower = Convert.ToBoolean(node.InnerText);
			node = doc.SelectSingleNode("Settings/GeneratePassAZUpper");
			if (node != null)
				GeneratePassAZUpper = Convert.ToBoolean(node.InnerText);
			node = doc.SelectSingleNode("Settings/GeneratePass09");
			if (node != null)
				GeneratePass09 = Convert.ToBoolean(node.InnerText);
			node = doc.SelectSingleNode("Settings/GeneratePassSymbols");
			if (node != null)
				GeneratePassSymbols = Convert.ToBoolean(node.InnerText);
		}

		private void SaveToRegistry() {
			using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegistryKey)) {
				key.SetValue("DataFolder", DataFolder);
				key.SetValue("GeneratedPassLength", GeneratedPassLength);
				key.SetValue("GeneratePassAZLower", GeneratePassAZLower? 1 : 0);
				key.SetValue("GeneratePassAZUpper", GeneratePassAZUpper ? 1 : 0);
				key.SetValue("GeneratePass09", GeneratePass09 ? 1 : 0);
				key.SetValue("GeneratePassSymbols", GeneratePassSymbols ? 1 : 0);
				key.SetValue("SyncExternal", SyncExternal ? 1 : 0);
				key.SetValue("SyncDrive", SyncDrive);
				key.SetValue("SyncStandalone", SyncStandalone ? 1 : 0);
			}
		}

		public void SaveToXML(string directory) {
			if (!directory.EndsWith(Path.DirectorySeparatorChar.ToString()))
				directory += Path.DirectorySeparatorChar;
			using (XmlTextWriter w = new XmlTextWriter(directory + XMLFile, new UTF8Encoding(false))) {
				w.Formatting = Formatting.Indented;
				w.WriteStartDocument();
				w.WriteStartElement("Settings");
				w.WriteElementString("GeneratedPassLength", GeneratedPassLength.ToString());
				w.WriteElementString("GeneratePassAZLower", GeneratePassAZLower.ToString());
				w.WriteElementString("GeneratePassAZUpper", GeneratePassAZUpper.ToString());
				w.WriteElementString("GeneratePass09", GeneratePass09.ToString());
				w.WriteElementString("GeneratePassSymbols", GeneratePassSymbols.ToString());
				w.WriteEndElement();
				w.WriteEndDocument();
			}
		}

		public string GeneratePassword() {
			if (GeneratedPassLength < 0)
				throw new Exception("Invalid configuration");

			List<char> set = new List<char>();
			if (GeneratePass09) {
				for (char c = '0'; c <= '9'; c++)
					set.Add(c);
			}
			if (GeneratePassAZLower) {
				for (char c = 'a'; c <= 'z'; c++)
					set.Add(c);
			}
			if (GeneratePassAZUpper) {
				for (char c = 'A'; c <= 'Z'; c++)
					set.Add(c);
			}
			if (GeneratePassSymbols) {
				set.Add('@');
				set.Add('~');
				set.Add('#');
				set.Add('$');
				set.Add('%');
				set.Add('*');
				set.Add('?');
			}

			char[] chararr = set.ToArray();
			RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
			byte[] buf = new byte[4];
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < GeneratedPassLength; i++) {
				rng.GetBytes(buf);
				uint randint = BitConverter.ToUInt32(buf, 0);
				sb.Append(chararr[randint % set.Count]);
			}
			return sb.ToString();
		}

		public int SyncDriveBit {
			get {
				return DriveBit(SyncDrive);
			}
		}

		public int DataDriveBit {
			get {
				return DriveBit(Path.GetPathRoot(DataFolder));
			}
		}

		private int DriveBit(string drive) {
			if (drive.Length != 3 || drive[0] < 'A' || drive[0] > 'Z')
				return 0;
			return (1 << (int)(drive[0] - 'A'));
		}
	}
}
